package com.example.guesswho.util

import android.content.Context
import android.net.Uri
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.concurrent.ConcurrentHashMap

/**
 * AudioPlayer utility class for playing short sound effects and easter eggs
 * Wraps ExoPlayer for simplified audio playback
 */
@UnstableApi
class AudioPlayer private constructor(private val context: Context) {
    
    private val exoPlayer: ExoPlayer = ExoPlayer.Builder(context).build()
    private val scope = CoroutineScope(Dispatchers.Main + Job())
    private val currentlyPlaying = ConcurrentHashMap<String, Job>()
    
    init {
        // Configure audio attributes for sound effects
        exoPlayer.setAudioAttributes(
            AudioAttributes.Builder()
                .setContentType(C.AUDIO_CONTENT_TYPE_SONIFICATION)
                .setUsage(C.USAGE_GAME)
                .build(),
            false
        )
        
        // Don't repeat by default for short sounds
        exoPlayer.repeatMode = Player.REPEAT_MODE_OFF
    }
    
    companion object {
        @Volatile
        private var instance: AudioPlayer? = null
        
        fun getInstance(context: Context): AudioPlayer {
            return instance ?: synchronized(this) {
                instance ?: AudioPlayer(context.applicationContext).also { instance = it }
            }
        }
        
        // Predefined sound resource IDs
        const val SOUND_SUCCESS = "success_sound"
        const val SOUND_FAILURE = "failure_sound"
        const val SOUND_EGG1 = "egg_sound1"
        const val SOUND_EGG2 = "egg_sound2"
        const val SOUND_EGG3 = "egg_sound3"
        const val SOUND_CELEBRATION = "celebration_sound"
    }
    
    /**
     * Play a sound from raw resources
     * @param soundResId Raw resource ID (e.g., R.raw.success_sound)
     * @param callback Optional callback for completion
     */
    fun playSound(soundResId: Int, callback: ((Boolean) -> Unit)? = null) {
        try {
            val soundKey = soundResId.toString()
            
            // Cancel any existing playback of the same sound
            currentlyPlaying[soundKey]?.cancel()
            
            val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(context)
            val mediaSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(MediaItem.fromUri("android.resource://${context.packageName}/$soundResId"))
            
            // Clear previous media items and add new one
            exoPlayer.clearMediaItems()
            exoPlayer.setMediaSource(mediaSource)
            exoPlayer.prepare()
            exoPlayer.play()
            
            // Create a job to monitor playback completion
            val job = scope.launch {
                var currentPosition = 0L
                val duration = exoPlayer.duration
                
                while (currentPosition < duration && exoPlayer.isPlaying) {
                    currentPosition = exoPlayer.currentPosition
                    delay(50)
                }
                
                currentlyPlaying.remove(soundKey)
                callback?.invoke(true)
            }
            
            currentlyPlaying[soundKey] = job
            
        } catch (e: Exception) {
            callback?.invoke(false)
        }
    }
    
    /**
     * Play a predefined sound by name
     * @param soundName Predefined sound name (e.g., SOUND_SUCCESS)
     * @param callback Optional callback for completion
     */
    fun playPredefinedSound(soundName: String, callback: ((Boolean) -> Unit)? = null) {
        val soundResId = when (soundName) {
            SOUND_SUCCESS -> getRawResourceId("success_sound")
            SOUND_FAILURE -> getRawResourceId("failure_sound")
            SOUND_EGG1 -> getRawResourceId("egg_sound1")
            SOUND_EGG2 -> getRawResourceId("egg_sound2")
            SOUND_EGG3 -> getRawResourceId("egg_sound3")
            SOUND_CELEBRATION -> getRawResourceId("celebration_sound")
            else -> null
        }
        
        soundResId?.let {
            playSound(it, callback)
        } ?: callback?.invoke(false)
    }
    
    /**
     * Play a random easter egg sound
     * @param callback Optional callback for completion
     */
    fun playRandomEasterEgg(callback: ((Boolean) -> Unit)? = null) {
        val eggSounds = listOf(SOUND_EGG1, SOUND_EGG2, SOUND_EGG3)
        val randomSound = eggSounds.random()
        playPredefinedSound(randomSound, callback)
    }
    
    /**
     * Play success sound with vibration feedback
     */
    fun playSuccessSound() {
        playPredefinedSound(SOUND_SUCCESS) { success ->
            if (success) {
                // Add haptic feedback if available
                try {
                    val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as android.os.Vibrator
                    if (vibrator.hasVibrator()) {
                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                            vibrator.vibrate(
                                android.os.VibrationEffect.createOneShot(
                                    100,
                                    android.os.VibrationEffect.DEFAULT_AMPLITUDE
                                )
                            )
                        } else {
                            @Suppress("DEPRECATION")
                            vibrator.vibrate(100)
                        }
                    }
                } catch (e: Exception) {
                    // Ignore vibration errors
                }
            }
        }
    }
    
    /**
     * Play failure sound
     */
    fun playFailureSound() {
        playPredefinedSound(SOUND_FAILURE)
    }
    
    /**
     * Play celebration sound (for special achievements)
     */
    fun playCelebrationSound() {
        playPredefinedSound(SOUND_CELEBRATION)
    }
    
    /**
     * Stop all currently playing sounds
     */
    fun stopAllSounds() {
        exoPlayer.stop()
        currentlyPlaying.values.forEach { it.cancel() }
        currentlyPlaying.clear()
    }
    
    /**
     * Check if a specific sound is currently playing
     */
    fun isPlaying(soundKey: String): Boolean {
        return currentlyPlaying.containsKey(soundKey) && exoPlayer.isPlaying
    }
    
    /**
     * Get current playback state
     */
    fun getPlaybackState(): Player.State {
        return exoPlayer.playbackState
    }
    
    /**
     * Set volume (0.0f to 1.0f)
     */
    fun setVolume(volume: Float) {
        exoPlayer.volume = volume.coerceIn(0f, 1f)
    }
    
    /**
     * Release player resources
     */
    fun release() {
        stopAllSounds()
        exoPlayer.release()
        instance = null
    }
    
    /**
     * Helper method to get raw resource ID by name
     */
    private fun getRawResourceId(resourceName: String): Int? {
        return try {
            val resId = context.resources.getIdentifier(
                resourceName,
                "raw",
                context.packageName
            )
            if (resId != 0) resId else null
        } catch (e: Exception) {
            null
        }
    }
}

/**
 * Extension function to simplify sound playback
 */
fun Context.playSound(soundResId: Int, callback: ((Boolean) -> Unit)? = null) {
    AudioPlayer.getInstance(this).playSound(soundResId, callback)
}

/**
 * Extension function to play predefined sounds
 */
fun Context.playPredefinedSound(soundName: String, callback: ((Boolean) -> Unit)? = null) {
    AudioPlayer.getInstance(this).playPredefinedSound(soundName, callback)
}